/*********************************************************************************
 *       Filename:  epoll_main.c
 *    Description:  
 *
 *        Version:  1.0.0(2024/12/02 20:14:09)
 *         Author:  Zhang Long <1318085470@qq.com>
 *      ChangeLog:  1, Release initial version on "2024/12/02 20:14:09"
 ********************************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <libgen.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <sqlite3.h>
#include <sys/epoll.h>
#include "database.h"
#include "ds18b20.h"
#include "transferdata.h"
#include "localtime.h"
#include "socket.h"
#include "logger.h"
#include "proc.h"


#define MAX_EVENTS 512
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
static inline void print_usage(char *progname)
{
    printf("%s usage: \n", progname);
    printf("-i(--ipaddr): specify server IP address(also not specify, default is all)\n");
    printf("-p(--port):   specify server PORT\n");
	printf("-d(--debug):  specify program running in debug mode\n");
    printf("-h(--help):   print this help information\n");

	printf("\nExample: %s -p 12345 -d\n", progname);

    return ;
}

int main(int argc,char **argv)
{

    int					log_level = LOG_LEVEL_INFO;

    int                 pack_bytes = 0;
    char			    *log_file = "server.log";
    char                *progname = NULL;
    int                 ch;
    char                *listen_ip = NULL;
	int			        listen_port = 0;   
    int                 daemon_run = 1; 
    long				log_size = 1000*1024;
    sqlite3 		    *db;
	char			    *db_file = "server.db";
    socket_t			sock = {0};
	int                 epoll_fd;
	struct epoll_event  event;
	struct epoll_event  event_array[MAX_EVENTS];
	int					events = 0;
	int 				i;
	int					connect_fd = -1;

	struct sockaddr_in  client_addr;
	socklen_t			client_addr_len = sizeof(client_addr);
	int					rv = -1;
	char				pack_buf[1024] = {0};
	packet_t			pack = {0};

    struct option opts[]=
    {
        {"ipaddr", required_argument, NULL, 'i'},
        {"port", required_argument, NULL, 'p'},
        {"debug", no_argument, NULL, 'd'},
        {"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
    };


    progname = basename(argv[0]);
    while ( -1 != (ch=getopt_long(argc, argv, "i:p:dh", opts, NULL)) )
	{
		switch (ch)
		{
			case 'i':
				listen_ip = optarg;
				break;

			case 'p':
				listen_port = atoi(optarg);
				break;

			case 'd':
				daemon_run = 0;
				log_file = "console";
				log_level = LOG_LEVEL_DEBUG;			
				break;

			case 'h':
				print_usage(progname);
				return 0;

			default:
				return -1;
		}
	}
    if( log_open(log_file, log_level, log_size, THREAD_LOCK_NONE) < 0 )
    {
        fprintf(stderr, "Initial log system failed\n");
        return 1;
    }
  	log_debug("log system open ok\n");
    if (!listen_port)
	{
		print_usage(progname);
		return -3;
	}
	log_debug("Parser arguments ok: IP[%s], PORT[%d], LOG[%s]\n", listen_ip, listen_port, log_file);

    set_socket_rlimit();
    install_signal();

    db_open(&db, db_file);

	db_create(db, TA_CREATE);
    
	if (daemon_run)
	{
		if (daemon(0, 0) < 0)
		{
			log_error("daemon() error: %s\n", strerror(errno));
			return -4;
		}
		else
		{
			log_info("Program running in daemon mode\n");
		}
	}

    socket_init(&sock, listen_ip, listen_port);
    if (socket_server_init(&sock) < 0)
	{
		log_error("socket_listen() error\n");
		return -5;
	}
	log_debug("Socket listen ok\n");
	
	if ( (epoll_fd=epoll_create(MAX_EVENTS)) < 0 )
	{
		log_error("epoll_create() error: %s\n", strerror(errno));
		return -6;
	}
	event.data.fd = sock.fd;
	event.events = EPOLLIN;
	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock.fd, &event) < 0)
	{
		log_error("epoll_ctl() error: %s\n", strerror(errno));
		return -7;
	}
	log_info("Server start to accept client...\n");
	while (!g_sigstop)
	{
		events = epoll_wait(epoll_fd, event_array, MAX_EVENTS, -1);
		if(events < 0)
 		{
 			log_error("epoll failure: %s\n", strerror(errno));
 			break;
 		}
		else if(0==events)
		{
			log_warn("Epoll get timeout\n");
			continue;
		}
		for (i=0; i<events; i++)
		{
			if(events<0)
			{
				log_error("epoll_wait() error: %s\n", strerror(errno));
				break;	
			}
			else if (0==events)
			{
				log_warn("Epoll get timeout\n");
				continue;
			}
			for(i=0;i<events;i++)
			{
				if ( (event_array[i].events&EPOLLERR) || (event_array[i].events&EPOLLHUP) )
				{
					log_error("Epoll get error on fd[%d]: %s\n", event_array[i].data.fd, strerror(errno));
					epoll_ctl(epoll_fd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
					close(event_array[i].data.fd);
				}
				if (sock.fd == event_array[i].data.fd)
				{
					if ( (connect_fd=accept(sock.fd, (struct sockaddr *)&client_addr, &client_addr_len)) < 0 )
					{
						log_error("accept() error: %s\n", strerror(errno));
						continue;	
					}
					event.data.fd = connect_fd;
					event.events = EPOLLIN;
					if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, connect_fd, &event) < 0)
					{
						log_error("epoll_ctl() error: %s\n", strerror(errno));
						close(connect_fd);
						continue;
					}

					log_info("Accept new socket[%d] client[%s:%d] ok\n", connect_fd, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
				}
				else
				{
					rv = socket_read(event_array[i].data.fd, pack_buf, sizeof(pack_buf));
					if (rv <= 0)
					{
						epoll_ctl(epoll_fd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
						close(event_array[i].data.fd);
						continue;
					}

					log_info("Read %d bytes data[%s] from socket[%d] client[%s:%d] ok\n", rv, pack_buf, event_array[i].data.fd, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
                
					packet_segmented_unpack(&pack, pack_buf);
					log_info("packet_segmented_unpack\n");
					write_to_db(db, &pack);	

				}
			}
		}
	}
	socket_close(&sock);
	db_close(db, log_file);
	log_close();
    return 0;
}


